import java.util.ArrayList;

/**
 * Die Klasse wertet die Funktion analytisch an Hand, der ihr uebergebenen Werte aus.
 * Es werden Null- und Extremstellen, sowie auch Randextrema und der Y-Achsen-Abschnitt errechnet.
 * @author c.barsch
 *
 */
public class Auswertung {

	private double werte[][];
	private double nst[];
	private double ext[][];
	// lre= linkes rand extremum
	private boolean lre;
	private boolean rre;

	private int zahl=0;
	
	public Auswertung(double[][] zwerte) {
		this.werte = zwerte;
		this.berechneNstUExt();
		for(int i=0; i<100; i++)
		{
			System.out.println("Ich werde ausgefuehrt!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
		}
		System.out.println("Es wurde so oft die richtung gewechselt: "+this.zahl);
	}

	/**
	 * Die Methode wird im Konstruktor der Auswertung aufgerufen. Sie berechnet alle Null- und Extremstellen
	 */
	private void berechneNstUExt() {
		ArrayList<Double> listnst = new ArrayList<Double>();
		ArrayList<Double> extx = new ArrayList<Double>();
		ArrayList<Double> exty = new ArrayList<Double>();

		int esize = 0;
		
		// um zu wissen ob die funktion zu beginn steigt oder faellt
		double vor1 = this.werte[1][1];
		int vor1x = 1;
		double vor2=Double.NaN;
		int vor2x=0;
		boolean firstR=false;
		boolean rauf=false;
		boolean rHasChanged=false;

	
		//es werden noch die x werte von ymin und ymax gebraucht
		int randyminx=0;
		int randymaxx=0;
		//raussuchen der beiden raender
		for(int i=1; i< werte.length; i++)
		{
			if(!Double.isNaN(werte[i][1]))
			{
				randyminx=i;
				break;
			}
		}
		for(int i=werte.length-1; i> 0; i--)
		{
			if(!Double.isNaN(werte[i][1]))
			{
				randymaxx=i;
				break;
			}
		}
		double ymin;
		double ymax;
		if(this.werte[randyminx][1] <= this.werte[randymaxx][1])
		{
			ymin = this.werte[randyminx][1];
			ymax = this.werte[randymaxx][1];
		}
		else
		{
			ymin = this.werte[randymaxx][1];
			ymax = this.werte[randyminx][1];
		}
		
		
		this.berechneNST(1, listnst);
		
		for (int i = 2; i < werte.length -1; i++) {
			
			if(Double.isNaN(this.werte[i][1]))
			{
				continue;
			}
			
			// Wenn der y-wert bis auf 5 nachkommastellen genau an 0 dran liegt,
			// wird dies als 0 stelle erkannt und gespeichert
			this.berechneNST(i, listnst);
			
			// speichert das groesste und kleinste y. wichtig fuer randextrema
			if (ymin > this.werte[i][1]) {
				ymin = this.werte[i][1];
			}
			if (ymax < this.werte[i][1]) {
				ymax = this.werte[i][1];
			}
			
			if(Double.isNaN(vor1))
			{
				vor1=this.werte[i][1];
				vor1x=i;
				continue;
			}
			else
			{
				vor2=this.werte[i][1];
				vor2x=i;
				if(!firstR)
				{
					rauf=(vor1 <= vor2);
					firstR=true;
				}
				if(rauf != (vor1 <= vor2))
				{
					rHasChanged=true;
				}
			}
			
			// Wenn die richtung sich aendert muss eine extremstelle da gewesen
			// sein
			if (rHasChanged) {
				// es wird davon ausgegangen, dass das extremum am letzten punkt
				// vor der richtungsaenderung liegt
				extx.add(this.werte[vor1x][0]);
				exty.add(vor1);
				// die "richtung" wird geaendert
				rauf = !rauf;
				this.zahl++;
				rHasChanged=false;
			}
			vor1=vor2;
			vor1x=vor2x;
		}
		
		//letzte NST muss noch geprueft werden ob sie nah genug an 0 liegt
		if (Math.abs(this.werte[this.werte.length-1][1]) <= 0.00001) {
			listnst.add(werte[this.werte.length-1][0]);
		}

		
		// einfuegen fuers letzte extremum
		if( (!Double.isNaN(this.werte[this.werte.length - 2][1])) && (!Double.isNaN(this.werte[this.werte.length - 1][1])))
		{
			
		
			if (rauf != (this.werte[this.werte.length - 2][1] <= this.werte[this.werte.length - 1][1])) {
				// es wird davon ausgegangen, dass das extremum am letzten punkt
				// vor der richtungsaenderung liegt
				extx.add(this.werte[this.werte.length - 2][0]);
				exty.add(this.werte[this.werte.length - 2][1]);
			}
		}

		esize += extx.size();

		this.nst = new double[listnst.size()];
		for (int i = 0; i < nst.length; i++) {
			this.nst[i] = listnst.get(i);
		}

		// pruefen ob ein Randextremum vorliegt
		if (this.werte[randyminx][1] <= ymin || this.werte[randyminx][1] >= ymax) {
			this.lre = true;
			esize++;
		}
		if (this.werte[randymaxx][1] <= ymin
				|| this.werte[randymaxx][1] >= ymax) {
			this.rre = true;
			esize++;
		}

		this.ext = new double[esize][2];
		for (int i = 0; i < extx.size(); i++) {
			this.ext[i][0] = extx.get(i);
			this.ext[i][1] = exty.get(i);
		}

		// einfuegen der randextrema
		this.randExt(lre, rre, esize, randyminx, randymaxx);
	}

	/**
	 * Die Funktion prueft und berechnet gegenenfalls die randExtrema
	 * @param l gibt an ob ein linksseitiges R-Extremum vorliegt
	 * @param r gibt an ob ein rechtssseitiges R-Extremum vorliegt
	 * @param esize gibt die Groesse des Arrays an
	 * @param randyminx gibt an, an welcher Stelle das Randextremum liegt
	 * @param randymaxx gibt an, an welcher Stelle das Randextremum liegt
	 */
	private void randExt(boolean l, boolean r, int esize, int randyminx, int randymaxx) {
		if (l && r) {
			this.ext[esize - 2][0] = this.werte[randyminx][0];
			this.ext[esize - 2][1] = this.werte[randyminx][1];
			this.ext[esize - 1][0] = this.werte[randymaxx][0];
			this.ext[esize - 1][1] = this.werte[randymaxx][1];
		} else if (l) {
			this.ext[esize - 1][0] = this.werte[randyminx][0];
			this.ext[esize - 1][1] = this.werte[randyminx][1];
		} else if (r) {
			this.ext[esize - 1][0] = this.werte[randymaxx][0];
			this.ext[esize - 1][1] = this.werte[randymaxx][1];
		}
	}

	/**
	 * Die Funktion berechnet die Nullstellen der Funktion
	 * @param gibt an, an welcher Stelle eine NST liegt
	 * @param in diese Arrayliste werden die NST eingefuegt
	 */
	private void berechneNST(int xnst, ArrayList<Double> listnst) {
		
		if(Double.isNaN(this.werte[xnst][1]) || Double.isNaN(this.werte[xnst+1][1]) )
		{
			return;
		}
		
		// es werden alle Stellen die mehr als 5 nachkommastellen haben als
		// 0-Stellen angesehen
		if (Math.abs(this.werte[xnst][1]) <= 0.00001) {
			listnst.add(werte[xnst][0]);
		}

		// xnst gibt an, welche indexwert von Werte[][] für ihn positiv und
		// ihn+1 negativ (oder umgekehrt) ist
		// Momentan wird hier von stetigkeit ausgegangen
		// Mit hilfe des 2. Strahlensatzes wird die NST berechnet

		if ((this.werte[xnst][1] > 0 && this.werte[xnst + 1][1] < 0)
				|| (this.werte[xnst][1] < 0 && this.werte[xnst + 1][1] > 0)) {

			double xabstand = Math.abs(this.werte[xnst][0]
					- this.werte[xnst + 1][0]);

			// hier + da ja ein wert positiv und ein wert negativ sein muss
			double yabstand = (Math.abs(this.werte[xnst][1] -this.werte[xnst + 1][1]));

			double ab = (Math.abs(this.werte[xnst][1]) / yabstand) * xabstand;
			
			listnst.add(this.werte[xnst][0]+ab);

		}

	}

	// gibt die uebergebenen Funtkionswerte wieder
	public double[][] getWerte() {
		return this.werte;
	}

	// gibt ein Array mit allen Nullstellen aus
	public double[] getNst() {
		return nst;
	}

	// gibt ein Array mit allen Extremwerten aus
	public double[][] getExt() {
		return ext;
	}

	// gibt den Y-Achsenabschnitt aus
	public double getYaa() {
		return werte[0][1];
	}

}